package modelo;

import java.util.ArrayList;
import java.util.List;

import modelo.Poligono;
import modelo.Ponto;
import modelo.Reta;

public class CortePoligono{

	private int xEsq;
	private int ySup; 
	private int xDir; 
	private int yInf;
	private Poligono poligono;

	public CortePoligono(Poligono poligono, Ponto p1, Ponto p2) {
		int x1 = p1.getX();
		int y1 = p1.getY();
		int x2 = p2.getX();
		int y2 = p2.getY();

		if (x1 > x2) {
			int aux = x1;
			x1 = x2;
			x2 = aux;
		}
		if (y1 < y2) {
			int aux = y1;
			y1 = y2;
			y2 = aux;
		}

		xEsq = x1;
		ySup = y1;

		xDir = x2;
		yInf = y2;

		this.poligono = poligono;
	}


	public Poligono corta() {
		List<Ponto> vertices = poligono.getPontos();
		List<Ponto> saida = new ArrayList<Ponto>();

		saida = cortaBorda(vertices, "esquerda");
		saida = cortaBorda(saida, "direita");
		saida = cortaBorda(saida, "superior");
		saida = cortaBorda(saida, "inferior");

		if (saida == null || saida.size() <= 0) {
			return null;
		} else {
			Poligono p = new Poligono(saida);
			return p;
		}
	}

	private List<Ponto> cortaBorda(List<Ponto> vertices, String borda) {

		List<Ponto> saida = new ArrayList<Ponto>();
		Ponto v1, v2;

		if (vertices == null || vertices.isEmpty())
			return null;

		v1 = vertices.get(0);

		for (int i = 1; i < vertices.size(); i++) {
			v2 = vertices.get(i);
			// caso 1 ou 4
			if (dentro(v2, borda))
				// caso 1
				if (dentro(v1, borda))
					saida.add(v2);
			// caso 4
				else {
					Ponto p = intercepta(v1, v2, borda);
					saida.add(p);
					saida.add(v2);
				}
			// caso 2 ou 3
			else
				// caso 2
				if (dentro(v1, borda)) {
					Ponto p = intercepta(v1, v2, borda);
					saida.add(p);
				}
			v1 = v2;
		}

		// testando ultimo vertice com o primeiro
		v2 = vertices.get(0);
		// caso 1 ou 4
		if (dentro(v2, borda))
			// caso 1
			if (dentro(v1, borda))
				saida.add(v2);
		// caso 4
			else {
				Ponto p = intercepta(v1, v2, borda);
				saida.add(p);
				saida.add(v2);
			}
		// caso 2 ou 3
		else
			// caso 2
			if (dentro(v1, borda)) {
				Ponto p = intercepta(v1, v2, borda);
				saida.add(p);
			}

		return saida;
	}

	private Ponto corteEsquerda(Reta reta) {
		return new Ponto(xEsq, (int) (reta.getM() * (float) xEsq + reta.getB()));

	}

	private Ponto corteDireita(Reta reta) {
		return new Ponto(xDir, (int) (reta.getM() * (float) xDir + reta.getB()));

	}

	private Ponto corteSuperior(Reta reta) {
		if (reta.getM() == Double.POSITIVE_INFINITY)
			return new Ponto(reta.getP1().getX(), ySup);
		else
			return new Ponto((int) (((float) ySup - reta.getB()) / reta.getM()), ySup);

	}

	private Ponto corteInferior(Reta reta) {
		// se for uma reta vertical retorna o proprio x da reta
		if (reta.getM() == Double.POSITIVE_INFINITY)
			return new Ponto(reta.getP1().getX(), yInf);
		else
			return new Ponto((int) (((float) yInf - reta.getB()) / reta.getM()), yInf);
	}

	private boolean dentro(Ponto v, String borda) {
		boolean dentro = false;

		if (borda.equals("superior"))
			if (v.getY() <= ySup)
				dentro = true;

		if (borda.equals("inferior"))
			if (v.getY() >= yInf)
				dentro = true;

		if (borda.equals("esquerda"))
			if (v.getX() >= xEsq)
				dentro = true;

		if (borda.equals("direita"))
			if (v.getX() <= xDir)
				dentro = true;

		return dentro;
	}

	private Ponto intercepta(Ponto p1, Ponto p2, String borda) {
		Ponto saida;
		Reta aux;

		aux = new Reta(p1, p2);

		if (borda == "esquerda") {
			saida = corteEsquerda(aux);
		} else if (borda == "direita") {
			saida = corteDireita(aux);
		} else if (borda == "inferior") {
			saida = corteInferior(aux);
		} else {
			saida = corteSuperior(aux);
		}

		return saida;
	}
}

